/**********************************************************************************************/
/* FBL_Starter_Mac.cpp                                                 						  */
/*                                                                       					  */
/* Copyright Paradigma, 1993-2006															  */
/* All Rights Reserved                                                   					  */
/**********************************************************************************************/

#include <VShared/FBL/publ/Headers/StdAfx.h>

#include <VShared/FBL/publ/Starter/FBL_Starter.h>

#include <VShared/FBL/publ/Util_classes/FBL_String.h>
#include <VShared/FBL/publ/Utilities/FBL_Util_Path.h>


/**********************************************************************************************/
#if FBL_MAC

#if FBL_MAC_MACHO
#define DLOPEN_NO_WARN
#include <dlfcn.h>	
#endif // FBL_MAC_MACHO 


/**********************************************************************************************/
FBL_Begin_Namespace


/**********************************************************************************************/
OSErr MakeParentSpec( const FSSpec* inSpec, FSSpec* outParentSpec )
{
	OSErr err = FSMakeFSSpec( inSpec->vRefNum, inSpec->parID, "\p", outParentSpec );
	
	return err;
}


/**********************************************************************************************/
OSErr MakeChildSpec( const FSSpec* inSpec, const char* inItemName, FSSpec* outChildSpec )
{
	long DirID;
	bool IsDir;
	GetDirectoryID( inSpec->vRefNum, inSpec->parID, inSpec->name, &DirID, &IsDir );

	Str255 pasName;
	c2pstrcpy(inItemName, pasName);

	OSErr err = FSMakeFSSpec( inSpec->vRefNum, DirID, pasName, outChildSpec );	
	
	return err;
}


#pragma mark -


/**********************************************************************************************/
CFragConnectionID LoadLibrary( const FSSpec* inSpec )
{
	CFragConnectionID handle;

#if FBL_MAC_MACHO

	FSRef ref;
	OSStatus err = FSpMakeFSRef( inSpec, &ref );

	UInt8 Path[1024];
	err = FSRefMakePath( &ref,	Path, 1024 );

	handle = (CFragConnectionID) dlopen( (char*) Path, 0 );

#else // FBL_MAC_MACHO

	Ptr myMainAddr ;
	Str255 myErrName ;

	OSErr err = GetDiskFragment( 
		inSpec, 0, kCFragGoesToEOF, "\p", kPrivateCFragCopy, &handle, &myMainAddr, myErrName );
		
	if( err != noErr )
		handle = NULL ;
		
#endif // FBL_MAC_MACHO	
	
	return handle;	
}
						
/**********************************************************************************************/
void* GetFunction( CFragConnectionID inLibrary, const char* inName )
{
	void *symbol = NULL;    // return vwalue
	
#if FBL_MAC_MACHO

	symbol = dlsym( inLibrary, inName );

#else // FBL_MAC_MACHO

	Ptr symAddress ;
	CFragSymbolClass symClass ;

	Str255 symName ;							
	FBL::c2pstrcpy( inName, (StringPtr) symName );	

	if ( FindSymbol( inLibrary , symName , &symAddress , &symClass ) == noErr )
		symbol = (void *)symAddress ;
	
#endif // FBL_MAC_MACHO	

	return symbol;
}
						

#pragma mark -


/**********************************************************************************************/
void FindAppFolder( FSSpec* outSpec )
{
#if !SKW_VX
	
	ProcessSerialNumber	psn;
	ProcessInfoRec		info;
	
	info.processAppSpec = outSpec;
	info.processInfoLength = sizeof(info);
	info.processName = nil;
	
#warning GetCurrentProcess() and GetProcessInformation() require Carbon - FIX ME
	::GetCurrentProcess(&psn);
	::GetProcessInformation(&psn,&info);  

	// now we have spec of app itself and go up to its directory.
	MakeParentSpec( outSpec, outSpec );
	
#endif // SKW_VX
}


/**********************************************************************************************/
// Define FSSpec for a given path folder.
//
#if FBL_MAC
//
bool GetFSpecOfFolder( char* inPathToFind, FSSpec* outSpec )
{
	FSRef ref;
	Boolean isDir;
	
	OSStatus err = FSPathMakeRef( (UInt8*) inPathToFind, &ref, &isDir );
	
	if( err == 0 )
	{
		FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL, outSpec, NULL );
		return true;
	}
	else
	{
		return false;
	}
}


/**********************************************************************************************/
// Returns the CARBON-style path for a given POSIX path.
//
bool GetPathOfFolder( char* inPathToFind, char* outPath, ulong inBuffSize )
{
	FSSpec spec;

	if( GetFSpecOfFolder( inPathToFind, &spec ) )
	{
		OSErr err = FSMakePath( spec.vRefNum, spec.parID, spec.name, outPath, inBuffSize );
		return bool( err == 0 );
	}
	else
	{
		return false;
	}
}

#endif // FBL_MAC

						
#pragma mark -


/**********************************************************************************************/
#if FBL_MAC_MACHO																// MACHO
// Macho version search the APP path.
// Macho version expect to see for now only this configuration:
//		my.app/contents/MacOS
//		my.app/contents/VComponents
//
void FindVcomponents( FSSpec* outCompSpec )
{
#if !SKW_VX
	
	OSErr err;

	// ---------------------------
	// APP/Contetns/VComponents 
	//
	FSSpec appSpec;
	FindAppFolder( &appSpec );
 
	ulong len = FBL::Min( appSpec.name[0], uchar(5) );
	if( memcmp( appSpec.name, "\pMacOS", 1 + len ) == 0 )
	{
		FSSpec componnetsSpec;
		err = MakeParentSpec( &appSpec, &componnetsSpec );
		
		// Components/VComponents
		err = MakeChildSpec( &componnetsSpec, "vcomponents", outCompSpec );		
		
		// if we have found VComponents inside of Contents folder, we return.
		// else we continue search for VComponents in other places.
		if( err == 0 )
			return;
	}	


	// -------------------------
	// check "/usr/local/lib/vcomponents"
	// 
	GetFSpecOfFolder( "/usr/local/lib/vcomponents", outCompSpec );

#endif // SKW_VX
}
#endif // FBL_MAC_MACHO


/**********************************************************************************************/
#if FBL_MAC_CARBON																// CARBON
//
void FindVcomponents( FSSpec* outCompSpec )
{
#if !SKW_VX
	
	// 1) APP folders:
	FSSpec appSpec;
	FindAppFolder( &appSpec );

	FSSpec resSpec;	
	OSErr err = MakeChildSpec( &appSpec, "vresources", &resSpec );
	
	if( err == 0 )	
	{
		memcpy( outCompSpec, &appSpec, sizeof(FSSpec) );
		return;
	}

	// 2) HOME/Library/CFMSupport
	// todo


	// 3) /Library/CFMSupport/VComponents
	GetFSpecOfFolder( "/Library/CFMSupport/vcomponents", outCompSpec );
	
#endif // SKW_VX
}
#endif // FBL_MAC_CARBON


#pragma mark -


/**********************************************************************************************/
extern "C" void InitBackpointers( void );


/**********************************************************************************************/
void Init_ValentinaDlls( bool inUseClientStrings )
{
#if FBL_STATIC || SKW_VX

	// This branch of code is used mainly by kernel_test project. And may be ONLY.
	// Here we just call GetStringFactory, because we do not have DLLs. 

	argused1( inUseClientStrings );
	InitBackpointers();

#else

	// KERNEL DLL string-factory has priority, i.e.
	// if sFactory is not NULL and call is from VCLIENT.DLL, we do not change pointers.
	// if sFactory is not NULL but call is from KERNEL.DLL, we will reset pointers to KERNEL strings. 
	// 
	if( String::sFactory && inUseClientStrings )
		return;

	FSSpec compSpec;
	FindVcomponents( &compSpec );
	
	FSSpec kernelSpec;
	MakeChildSpec( &compSpec, GetKernelDllName(), &kernelSpec );

	// We at first try to load KERNEL.DLL 
	CFragConnectionID dll = 0;
	bool f = inUseClientStrings || (dll = LoadLibrary(&kernelSpec)) == NULL; 
	if( f )
	{
		FSSpec clientSpec;
		MakeChildSpec( &compSpec, GetClientDllName(), &clientSpec );

		// Try to load client implementation.
		dll = LoadLibrary( &clientSpec ); 
		if( !dll )
		{
			// CRITICAL SITUATION: neither kernel nor client dll was not found!
			// Valentina cannot continue to work.
			// FIXME: throw exception!
			return;
		}
	}

	// --------------------------------
	// Get init callbacks function.
	typedef void (*InitBackpointers_PTR)( void );
	InitBackpointers_PTR pfn;

	// Store reference to the function and call it:
	pfn = (InitBackpointers_PTR) GetFunction( dll, "InitBackpointers" );
	pfn();

#endif // FBL_STATIC || SKW_VX

}


/**********************************************************************************************/
FBL_End_Namespace

#endif // FBL_MAC
